Skip to main content

Overview

JoggAI sends HTTP POST requests to your webhook endpoint when events occur (like video completion). This eliminates the need for polling and provides instant notifications.
Webhook Limit: Each user can create up to 20 webhook endpoints.

Real-Time Updates

Get notified instantly when videos complete

Reduced API Calls

No need to poll for status

Better UX

Respond to events immediately

Scalable

Handle high volumes efficiently

Workflow Overview

Webhook requests expect a 200 OK response within 5 seconds. Non-2xx responses trigger automatic retries.

Quick Start

Step 1: Add Webhook Endpoint

curl --location --request POST 'https://api.jogg.ai/v2/endpoint' \
  --header 'x-api-key: YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "url": "https://example.com/webhook",
    "events": ["generated_avatar_video_success", "generated_avatar_video_failed"],
    "status": "enabled"
  }'
Response:
{
  "code": 0,
  "msg": "Success",
  "data": {
    "endpoint_id": "wh_123456789",
    "url": "https://example.com/webhook",
    "secret": "whsec_abc123xyz",
    "status": "enabled",
    "events": ["generated_avatar_video_success", "generated_avatar_video_failed"],
    "username": "johndoe",
    "created_at": 1732806631
  }
}
Save the secret - you’ll use it to verify webhook authenticity!

Step 2: Verify Webhook Signature

Always verify that webhooks come from JoggAI:
func VerifyWebhookSignature(payload []byte, signature, secret string) bool {
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write(payload)
    expectedSignature := hex.EncodeToString(mac.Sum(nil))
    return hmac.Equal([]byte(signature), []byte(expectedSignature))
}
import hmac
import hashlib

def verify_webhook_signature(payload, signature, secret):
    expected = hmac.new(
        secret.encode('utf-8'),
        payload,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, expected)
const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
    const expected = crypto
        .createHmac('sha256', secret)
        .update(payload)
        .digest('hex');
    return crypto.timingSafeEqual(
        Buffer.from(signature),
        Buffer.from(expected)
    );
}
verify_webhook() {
  local payload="$1"
  local signature="$2"
  local secret="$3"
  
  expected=$(echo -n "$payload" | openssl dgst -sha256 -hmac "$secret" | awk '{print $2}')
  
  if [ "$signature" = "$expected" ]; then
    echo "✅ Signature verified"
    return 0
  else
    echo "❌ Invalid signature"
    return 1
  fi
}

Webhook Events

Available Events

EventDescriptionWhen Triggered
generated_video_successVideo generation succeededVideo is ready to download
generated_video_failedVideo generation failedAn error occurred during generation
generated_avatar_video_successAvatar video generation succeededVideo is ready to download
generated_avatar_video_failedAvatar video generation failedAn error occurred during generation
generated_product_video_successProduct video generation succeededVideo is ready to download
generated_product_video_failedProduct video generation failedAn error occurred during generation
generated_template_video_successTemplate video generation succeededVideo is ready to download
generated_template_video_failedTemplate video generation failedAn error occurred during generation
generated_translate_video_successVideo translation succeededTranslated video is ready
generated_translate_video_failedVideo translation failedAn error occurred during translation
create_avatar_successAvatar creation succeededAvatar is ready to use
create_avatar_failedAvatar creation failedAn error occurred during avatar creation
generated_photo_avatar_successPhoto avatar creation succeededAvatar is ready to use
generated_photo_avatar_failedPhoto avatar creation failedAn error occurred during avatar creation
generated_product_avatar_successProduct avatar creation succeededAvatar is ready to use
generated_product_avatar_failedProduct avatar creation failedAn error occurred during avatar creation
generated_script_successAI script generation succeededScripts are ready to use
generated_script_failedAI script generation failedAn error occurred during script generation
generated_image_successImage generation succeededGenerated images are ready
generated_image_failedImage generation failedAn error occurred during image generation
generated_motion_successMotion generation succeededGenerated motion video is ready
generated_motion_failedMotion generation failedAn error occurred during motion generation
View List Webhook Events for the complete list of events.

Event Payloads

{
  "event_id": "evt_123456789",
  "event": "generated_avatar_video_success",
  "timestamp": 1732806631,
  "data": {
    "video_id": "video_123456",
    "status": "completed",
    "video_url": "https://res.jogg.ai/video.mp4",
    "cover_url": "https://res.jogg.ai/cover.jpg",
    "duration": 30
  }
}
{
  "event_id": "evt_123456789",
  "event": "generated_avatar_video_failed",
  "timestamp": 1732806631,
  "data": {
    "video_id": "video_123456",
    "status": "failed",
    "error": {
      "code": 50000,
      "message": "Video generation failed"
    }
  }
}
{
  "event_id": "evt_987654321",
  "event": "generated_photo_avatar_success",
  "timestamp": 1732806631,
  "data": {
    "avatar_id": 123,
    "status": "completed",
    "preview_url": "https://res.jogg.ai/avatar-preview.mp4",
    "cover_url": "https://res.jogg.ai/avatar-cover.jpg"
  }
}
{
  "event": "generated_image_success",
  "timestamp": 1732806631,
  "data": {
    "photo_id": "photo_123456",
    "photo_urls": [
      "https://res.jogg.ai/image1.jpg",
      "https://res.jogg.ai/image2.jpg"
    ]
  }
}
{
  "event": "generated_image_failed",
  "timestamp": 1732806631,
  "data": {
    "photo_id": "photo_123456",
    "error": "Image generation failed due to invalid parameters"
  }
}
{
  "event": "generated_motion_success",
  "timestamp": 1732806631,
  "data": {
    "motion_id": "motion_123456",
    "motion_url": "https://res.jogg.ai/motion.mp4",
    "photo_url": "https://res.jogg.ai/photo.jpg"
  }
}
{
  "event": "generated_motion_failed",
  "timestamp": 1732806631,
  "data": {
    "motion_id": "motion_123456",
    "error": "Motion generation failed due to processing error"
  }
}

Managing Webhooks

curl --request GET 'https://api.jogg.ai/v2/endpoints' \
  --header 'x-api-key: YOUR_API_KEY'
Response includes all configured webhook endpoints with their settings.See details: List Webhook Endpoints
curl --request PUT 'https://api.jogg.ai/v2/endpoint/wh_123456789' \
  --header 'x-api-key: YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "url": "https://new-url.com/webhook",
    "events": ["generated_avatar_video_success", "generated_avatar_video_failed", "generated_photo_avatar_success"],
    "status": "enabled"
  }'
Note: The secret cannot be modified.See details: Update Webhook Endpoint
curl --request DELETE 'https://api.jogg.ai/v2/endpoint/wh_123456789' \
  --header 'x-api-key: YOUR_API_KEY'
See details: Delete Webhook Endpoint
curl --request GET 'https://api.jogg.ai/v2/events' \
  --header 'x-api-key: YOUR_API_KEY'
Returns list of all supported webhook events.See details: List Webhook Events

Security Requirements

Request Headers

POST /webhook HTTP/1.1
Host: your-domain.com
Content-Type: application/json
X-Webhook-Event: generated_avatar_video_success
X-Webhook-Signature: 7256c87be255861cbbe92f4a04a4500176b045a287f258e32e5b6c6b96d7f290
User-Agent: JoggAI-Webhook/2.0

Security Checklist

  • ✅ All webhook URLs must use HTTPS
  • ✅ Verify X-Webhook-Signature header on every request
  • ✅ Use HMAC SHA-256 with your secret key
  • ✅ Signature is computed on raw request body
  • ✅ Use constant-time comparison to prevent timing attacks
  • ✅ Keep webhook secret secure (environment variables)
  • ✅ Rotate secrets periodically
Always verify signatures before processing webhooks. Reject requests with invalid signatures and log suspicious attempts.